#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

#include "interfaces.h" // Must be included after winioctl.h
#include "direct_io.h"
#include "error_msg.h"
#include "flash.h"
#include "programmer.h"

#define DRIVER_NAME       "winflashrom"
unsigned int cpu_thread_id = 0;
//
// verbosity of messages
//
#define VEBOSE_DEBUG_MESSAGE
#undef VEBOSE_DEBUG_MESSAGE
#include<Psapi.h>
//
// Globals
//
static HANDLE h_device = INVALID_HANDLE_VALUE;
static UCHAR driver_path[MAX_PATH];
PERFORMANCE_INFORMATION stPerformance;
int g_dbg_lvl = DBG_LEVEL_PROG;

//
// Function implementation
//

static BOOLEAN setup_driver_name( PUCHAR driver_path )
{
    HANDLE file_handle;
    unsigned long err_no;
    unsigned long driver_path_len = 0;

    //
    // Get the current directory.
    //
    driver_path_len = GetCurrentDirectory(MAX_PATH, driver_path );

    if (!driver_path_len) {
	err_no = GetLastError();	    
        DBG_INFO("GetCurrentDirectory failed!  Error = %d \n",(int) err_no);
				display_error_message(err_no);

        return FALSE;
    }

    //
    // Setup path name to driver file.
    //

    strcat(driver_path, "\\");
    strcat(driver_path, DRIVER_NAME);
    strcat(driver_path, ".sys");

    //
    // Insure driver file is in the specified directory.
    //

    if ((file_handle = CreateFile(driver_path,
                                 GENERIC_READ | GENERIC_WRITE,
                                 0,
                                 NULL,
                                 OPEN_EXISTING,
                                 FILE_ATTRIBUTE_NORMAL,
                                 NULL
                                 )) == INVALID_HANDLE_VALUE) {


        DBG_INFO("Driver: BIOS_PROBE.SYS is not in the system directory. \n");

        //
        // Indicate failure.
        //

        return FALSE;
    }

    //
    // Close open file handle.
    //

    if (file_handle) {

        CloseHandle(file_handle);
    }

    //
    // Indicate success.
    //

    return TRUE;


}   // setup_driver_name


static BOOL extract_driver(char *sz_driver, char *sz_out)
/*++
Routine Description:
	find, load and extract the driver from resource

Arguments: 
	sz_driver = resource name or number
	sz_out    = output filename
    
Return Value:
	FALSE = function fails
	TRUE = function succeds
--*/

{
	HRSRC h_res;
	HGLOBAL h_sys;
	LPVOID p_sys;
	HANDLE h_file;
	unsigned long dw_size, dw_wri;

	// find and load resources, return false if failed
	if(!(h_res = FindResource(NULL, sz_driver, TEXT("DRIVER"))))
		return FALSE;

	if(!(h_sys = LoadResource(0, h_res)))
		return FALSE;

	dw_size = SizeofResource(0, h_res);
	p_sys = LockResource(h_sys);

	// create the driver file to the choosen path
	if((h_file = CreateFile(sz_out, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0)) == INVALID_HANDLE_VALUE)
		return FALSE;

	// write the driver and and close the file handle
	if(!WriteFile(h_file, p_sys, dw_size, &dw_wri, 0))
	{
		CloseHandle(h_file);
	 	return FALSE;
	}
	
	CloseHandle(h_file);
	return TRUE;
}


static BOOL activate_driver(char *sz_serv, char *sz_path, BOOL activate)
/*++
Routine Description:
	register, run, stop and unregister driver

Arguments: 
	sz_serv   = driver service name
	sz_path   = path to driver
	activate = register/unregisterNone
    
Return Value:
	0 = function fails
	1 = function succeds
--*/
{
	BOOL b_stat;
	SC_HANDLE h_sc, h_sr;
	SERVICE_STATUS srv_stat;

	b_stat = FALSE;
	
	h_sc = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if(!h_sc) 
	{
	    DBG_INFO("Error: failed to open service control manager\n");
	    return FALSE;
	}

	if(activate)
	{
	    // start debug		
 	    DBG_INFO("creating new service... \n");
	    // end debug
	    DBG_INFO("creating new service... %s  %s\n",sz_serv,sz_path);
	    h_sr = CreateService(h_sc, sz_serv, sz_serv, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, 
				SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, sz_path, 
				NULL, NULL, NULL, NULL, NULL);
		
	    if(!h_sr) 
	    {
	    	// start debug		
 	        	DBG_INFO("invalid service handle... \n");
	    	// end debug
					
				if(GetLastError() == ERROR_SERVICE_EXISTS)
				{
					h_sr = OpenService(h_sc, sz_serv, SERVICE_ALL_ACCESS);
					
					if(!h_sr)
					{
							DBG_INFO("Error: failed to open access to the service (service already exists)\n");
								
							CloseServiceHandle(h_sc);
							return FALSE;
					}
					else
					{
						 /*
						 if (! DeleteService(h_sr) ) 
    					 {
      						  DBG_INFO("DeleteService failed (%d)\n", GetLastError()); 
   						 }
  						  else DBG_INFO("Service deleted successfully\n"); 
							*/
						DBG_INFO( "OpenService() ok ! \n" );
					}
					b_stat = StartService(h_sr, 0, NULL);
					if(b_stat == 0)
					{
						if(ERROR_SERVICE_ALREADY_RUNNING != GetLastError())
						{    
							CloseServiceHandle(h_sc);
							CloseServiceHandle(h_sr);
							DBG_INFO( "StartService() ERROR_SERVICE_ALREADY_RUNNING ok  %d ! \n" ,GetLastError());
							return FALSE;
						}
						b_stat = TRUE;
					}
					else
					{
						DBG_INFO( "StartService () b_stat != 0  ok ! \n" );
						b_stat = TRUE;
					}
					DBG_INFO("Error: service already exist!\n");
				}   
				else
				{
					// start debug
					DBG_INFO("CreateService failed. But, not because of service exists :(\n");
					// end debug
					
					CloseServiceHandle(h_sc);
					return FALSE;
				}
	    }
	    else
	    {
			if(StartService(h_sr, 0, NULL) == 0) 
			{
				if(ERROR_SERVICE_ALREADY_RUNNING != GetLastError())
				{    
							CloseServiceHandle(h_sc);
							CloseServiceHandle(h_sr);
							return FALSE;
				}
			} 
			else 
			{
				b_stat = TRUE;
			}
	    }
	}
	else
	{
		if(!(h_sr = OpenService(h_sc, sz_serv, SERVICE_ALL_ACCESS)))
			b_stat = FALSE;
		else
			b_stat = (ControlService(h_sr, SERVICE_CONTROL_STOP, &srv_stat) == 0) ? FALSE : TRUE;

		// unregister the driver
		b_stat = (DeleteService(h_sr) == 0) ? FALSE : TRUE;
	}

	// close the service handle
	if(h_sr) CloseServiceHandle(h_sr);
	if(h_sc) CloseServiceHandle(h_sc);
	return b_stat;
}


int init_driver()
/*++
Routine Description:
	Initialize the driver interface

Arguments: None
    
Return Value:
    0 if error
    1 if succeeded
--*/
{
	unsigned long err_no;

	// 
    // Extract the driver binary from the resource in the executable
    // 
    if (extract_driver(TEXT("WINFLASHROMDRV"), "winflashrom.sys") == TRUE) {
		DBG_INFO("The driver has been extracted\n");
		
    } else {
		display_error_message(GetLastError());
		DBG_INFO("Exiting..\n");
		return 0;
    }
    
    
    //
    // Setup full path to driver name.
    //
    if (!setup_driver_name(driver_path)) {
		DBG_INFO("Error: failed to setup driver name \n");
        return 0;
    }


    //
    // Try to activate the driver 
    //
    if(activate_driver(DRIVER_NAME, driver_path, TRUE) == TRUE) {
		DBG_INFO("The driver is registered and activated\n"); 
    } else {
		DBG_INFO("Error: unable to register and activate the driver\n");
		// DeleteFile(driver_path);
		return 0;		
    }

    //
    // Try to open the newly installed driver.
    //
        
    h_device = CreateFile( "\\\\.\\winflashrom",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                NULL);

    if ( h_device == INVALID_HANDLE_VALUE ){
        err_no = GetLastError();	
		DBG_INFO ( "Error: CreateFile Failed : %d\n", (int) err_no );
		display_error_message(err_no);

		// cleanup resurces created and used up to now
		activate_driver(DRIVER_NAME, driver_path, FALSE);
		DeleteFile(driver_path);
	
        return 0;
    }
    int cb = sizeof(stPerformance);
   // GetPerformanceInfo(&stPerformance, cb);
	return 1;
}


void cleanup_driver()
{
    //
    // Close the access to the exported device interface in user-mode
    //
	if(h_device != INVALID_HANDLE_VALUE) {
		CloseHandle(h_device); 
	}

    //
    // Unload the driver.  Ignore any errors.
    //
    if(activate_driver(DRIVER_NAME, driver_path, FALSE) == TRUE) {
		DBG_INFO("The driver stopped and unloaded\n"); 
    } else {
		DBG_INFO("Error: failed to stop and unload the driver\n");
    }

    //
    // Delete the driver file
    // 
    DeleteFile(driver_path);
}



unsigned char inb(unsigned short port)
{
    IO_BYTE ioBuf;
    unsigned long bytes_returned;
    
    ioBuf.port8 = port; // port address to read from
    

    if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(inl) Error: the driver handle is invalid!\n");
	return -1;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_READ_PORT_BYTE,
				 &ioBuf,
				 sizeof(ioBuf),
				 &ioBuf,
				 sizeof(ioBuf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
	return -1;
    }

#ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_READ_PORT_BYTE) : %d\n", 
	    bytes_returned);
#endif //VEBOSE_DEBUG_MESSAGE

    return ioBuf.value8;
}


void outb(unsigned char value, unsigned short port)
{
    IO_BYTE ioBuf;
    unsigned long bytes_returned;
    
    ioBuf.port8 = port; // port address to write to
    ioBuf.value8 = value; 

    if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(outw) Error: the driver handle is invalid!\n");
	return;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_WRITE_PORT_BYTE,
				 &ioBuf,
				 sizeof(ioBuf),
				 &ioBuf,
				 sizeof(ioBuf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
    }
    
 #ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_WRITE_PORT_BYTE) : %d\n", 
	    bytes_returned);
 #endif //VEBOSE_DEBUG_MESSAGE
}


unsigned short inw(unsigned short port)
{
    IO_WORD ioBuf;
    unsigned long bytes_returned;
    
    ioBuf.port16 = port; // port address to read from
    

    if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(inl) Error: the driver handle is invalid!\n");
	return -1;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_READ_PORT_WORD,
				 &ioBuf,
				 sizeof(ioBuf),
				 &ioBuf,
				 sizeof(ioBuf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
	return -1;
    }

#ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_READ_PORT_WORD) : %d\n", 
	    bytes_returned);
#endif //VEBOSE_DEBUG_MESSAGE

    return ioBuf.value16;
}


void outw(unsigned short value, unsigned short port)
{
    IO_WORD ioBuf;
    unsigned long bytes_returned;
    
    ioBuf.port16 = port; // port address to write to
    ioBuf.value16 = value; 

    if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(outw) Error: the driver handle is invalid!\n");
	return;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_WRITE_PORT_WORD,
				 &ioBuf,
				 sizeof(ioBuf),
				 &ioBuf,
				 sizeof(ioBuf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
    }

#ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_WRITE_PORT_WORD) : %d\n", 
	    bytes_returned);
#endif //VEBOSE_DEBUG_MESSAGE
}


unsigned long inl(unsigned short port)
{
    IO_LONG ioBuf;
    unsigned long bytes_returned;
    
    ioBuf.port32 = port; // port address to read from
    

    if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(inl) Error: the driver handle is invalid!\n");
	return -1;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_READ_PORT_LONG,
				 &ioBuf,
				 sizeof(ioBuf),
				 &ioBuf,
				 sizeof(ioBuf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
	return -1;
    }

	 #ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_READ_PORT_LONG) : %d\n", 
	    bytes_returned);
	 #endif //VEBOSE_DEBUG_MESSAGE

    return ioBuf.value32;
}


void outl(unsigned long value, unsigned short port)
{
    IO_LONG ioBuf;
    unsigned long bytes_returned;
    
    ioBuf.port32 = port; // port address to write to
    ioBuf.value32 = value; 

    if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(outl) Error: the driver handle is invalid!\n");
	return;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_WRITE_PORT_LONG,
				 &ioBuf,
				 sizeof(ioBuf),
				 &ioBuf,
				 sizeof(ioBuf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
    }

 #ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_WRITE_PORT_LONG) : %d\n", 
	    bytes_returned);
 #endif //VEBOSE_DEBUG_MESSAGE
}

int setup_cpu_msr(int cpu)
{
	cpu_thread_id = cpu;
	return 0;
}
void cleanup_cpu_msr(void)
{
	cpu_thread_id = 0;
	return 0;
}

int wrmsr(int addr, msr_t msr)
{
	unsigned long u32Buf[4];
	u32Buf[0] = cpu_thread_id;
	u32Buf[1] = addr;
	u32Buf[2] = msr.lo;
	u32Buf[3] = msr.hi;
	unsigned long bytes_returned;

	if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(outl) Error: the driver handle is invalid!\n");
	return;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_WRMSR,
				 &u32Buf,
				 sizeof(u32Buf),
				 &u32Buf,
				 sizeof(u32Buf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
	return -1;
    }

	return 0;
}

msr_t rdmsr(int addr)
{
	unsigned long u32Buf[2];
	u32Buf[0] = cpu_thread_id;
	u32Buf[1] = addr;
	unsigned long bytes_returned;
	msr_t stmsr = {0};

	if( INVALID_HANDLE_VALUE == h_device) {
	DBG_INFO("(outl) Error: the driver handle is invalid!\n");
	return stmsr;
    }
    
    if( FALSE == DeviceIoControl( h_device, 
				 IOCTL_RDMSR,
				 &u32Buf,
				 sizeof(u32Buf),
				 &u32Buf,
				 sizeof(u32Buf),
				 &bytes_returned,
				 NULL))
    {
	display_error_message(GetLastError());
	return stmsr;
    }

	stmsr.lo = u32Buf[0];
	stmsr.hi = u32Buf[1];
	return stmsr;

}
void* map_physical_addr_range( unsigned long phy_addr_start, unsigned long size )
/*++
Routine Description:
	Maps an MMIO range.

Arguments:
    phy_addr_start -  The start of physical address to be mapped.
	size - The size of the MMIO range to map, in bytes.

Return Value:
    NULL - on error
	pointer to the virtual address of the mapped MMIO range - on success

Note:
	-	The pointer returned from this function is in the context of the 
	    usermode application that use this function.
	-	You must call unmap_physical_addr_range when you are done with the  
		mapped physical memory/MMIO range that you obtain with this function.
--*/
{
	MMIO_MAP mmio_map;
	unsigned long bytes_returned;

	if( INVALID_HANDLE_VALUE == h_device) {
		DBG_INFO("(map_physical_addr_range) Error: the driver handle is invalid!\n");
		return 0; // error
    }
    
	mmio_map.phy_addr_start = phy_addr_start;
	mmio_map.size = size;

    if( FALSE == DeviceIoControl( h_device, 
					IOCTL_MAP_MMIO,
					&mmio_map,
					sizeof(mmio_map),
					&mmio_map,
					sizeof(mmio_map),
					&bytes_returned,
					NULL))
    {
		display_error_message(GetLastError());
		return NULL; // error
    }

 #ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_MAP_BIOS_CHIP) : %d\n", 
	    bytes_returned);
 #endif //VEBOSE_DEBUG_MESSAGE

    return mmio_map.usermode_virt_addr; 
}	


int unmap_physical_addr_range( void* virt_addr_start, unsigned long size )
/*++
Routine Description:
	Unmaps a previously mapped MMIO range.

Arguments:
    virt_addr_start - The start address of the mapped physical address.
	                This parameter _must_ _be_ in the context of the 
					currently executing application. It must be a 
					pointer returned from a previous call to 
					map_physical_addr_range function.
	size - The size of the mapped MMIO range in bytes.

Return Value:
    0 - on error
	1 - on success

Note:
	This function must be called when you are done with a 
	mapped physical memory/MMIO range.
--*/
{
	MMIO_MAP mmio_map;
	unsigned long bytes_returned;

	if( INVALID_HANDLE_VALUE == h_device) {
		DBG_INFO("(unmap_physical_addr_range) Error: the driver handle is invalid!\n");
		return 0; // error
    	}
    
	mmio_map.usermode_virt_addr = virt_addr_start;
	mmio_map.size = size;

    if( FALSE == DeviceIoControl( h_device, 
					IOCTL_UNMAP_MMIO,
					&mmio_map,
					sizeof(mmio_map),
					&mmio_map,
					sizeof(mmio_map),
					&bytes_returned,
					NULL))
    {
		display_error_message(GetLastError());
		return 0; // error
    }

 #ifdef VEBOSE_DEBUG_MESSAGE
    DBG_INFO("number of bytes returned from kernel (IOCTL_UNMAP_BIOS_CHIP) : %d\n", 
	    bytes_returned);
 #endif //VEBOSE_DEBUG_MESSAGE

	return 1; // success
}
// port will start  =============================================================================================


/*static unsigned long getpagesize()
{
	return stPerformance.PageSize;
}*/
static uintptr_t round_to_page_boundaries(uintptr_t *start, size_t *len)
{
	uintptr_t page_size = getpagesize();
	uintptr_t page_mask = ~(page_size-1);
	uintptr_t end = *start + *len;
	uintptr_t old_start = *start;
	//msg_gspew("page_size=%" PRIxPTR "\n", page_size);
//	msg_gspew("pre-rounding:  start=0x%0*" PRIxPTR ", len=0x%zx, end=0x%0*" PRIxPTR "\n", PRIxPTR_WIDTH, *start, *len, PRIxPTR_WIDTH, end);
	*start = *start & page_mask;
	end = (end + page_size - 1) & page_mask;
	*len = end - *start;
	//msg_gspew("post-rounding: start=0x%0*" PRIxPTR ", len=0x%zx, end=0x%0*" PRIxPTR "\n", PRIxPTR_WIDTH, *start, *len, PRIxPTR_WIDTH, *start + *len);
	return old_start - *start;
}

static void *sys_physmap_rw_uncached(uintptr_t phys_addr, size_t len)
{
	void *virt_addr;
    virt_addr =  map_physical_addr_range(phys_addr,len);
	return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr;
} 
static void *physmap_common(const char *descr, uintptr_t phys_addr, size_t len, bool readonly, bool autocleanup,
			    bool round)
{
	void *virt_addr;
	uintptr_t offset = 0;

	if (len == 0) {
		//msg_pspew("Not mapping %s, zero size at 0x%0*" PRIxPTR ".\n", descr, PRIxPTR_WIDTH, phys_addr);
		return ERROR_PTR;
	}

	if (round)
		offset = round_to_page_boundaries(&phys_addr, &len);

	if (readonly)
		virt_addr = sys_physmap_rw_uncached(phys_addr, len);
	else
		virt_addr = sys_physmap_rw_uncached(phys_addr, len);

	if (ERROR_PTR == virt_addr) {
		if (NULL == descr)
			descr = "memory";
		//msg_perr("Error accessing %s, 0x%zx bytes at 0x%0*" PRIxPTR "\n", descr, len, PRIxPTR_WIDTH, phys_addr);
	//	msg_perr(MEM_DEV " mmap failed: %s\n", strerror(errno));
#ifdef __linux__
		if (EINVAL == errno) {
			msg_perr("In Linux this error can be caused by the CONFIG_NONPROMISC_DEVMEM (<2.6.27),\n");
			msg_perr("CONFIG_STRICT_DEVMEM (>=2.6.27) and CONFIG_X86_PAT kernel options.\n");
			msg_perr("Please check if either is enabled in your kernel before reporting a failure.\n");
			msg_perr("You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n");
			msg_perr("disabling the other option unfortunately requires a kernel recompile. Sorry!\n");
		}
#elif defined (__OpenBSD__)
		msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
			 "and reboot, or reboot into\n"
			 "single user mode.\n");
#endif
		return ERROR_PTR;
	}
#if 0
	if (autocleanup) {
		struct undo_physmap_data *d = malloc(sizeof(*d));
		if (d == NULL) {
			msg_perr("%s: Out of memory!\n", __func__);
			physunmap_unaligned(virt_addr, len);
			return ERROR_PTR;
		}

		d->virt_addr = virt_addr;
		d->len = len;
		if (register_shutdown(undo_physmap, d) != 0) {
			msg_perr("%s: Could not register shutdown function!\n", __func__);
			physunmap_unaligned(virt_addr, len);
			return ERROR_PTR;
		}
	}
#endif
	return virt_addr + offset;
}

static int release_io_perms(void *p)
{
	return 0;
}

int rget_io_perms(void)
{
	
    register_shutdown(release_io_perms, NULL);
	
	return 0;
}

void *physmap(const char *descr, uintptr_t phys_addr, size_t len)
{
	return physmap_common(descr, phys_addr, len, PHYSM_RW, PHYSM_NOCLEANUP, PHYSM_ROUND);
}

void *rphysmap(const char *descr, uintptr_t phys_addr, size_t len)
{
	return physmap_common(descr, phys_addr, len, PHYSM_RW, PHYSM_CLEANUP, PHYSM_ROUND);
}

void *physmap_ro(const char *descr, uintptr_t phys_addr, size_t len)
{
	return physmap_common(descr, phys_addr, len, PHYSM_RO, PHYSM_NOCLEANUP, PHYSM_ROUND);
}

void *physmap_ro_unaligned(const char *descr, uintptr_t phys_addr, size_t len)
{
	return physmap_common(descr, phys_addr, len, PHYSM_RO, PHYSM_NOCLEANUP, PHYSM_EXACT);
}



static void sys_physunmap_unaligned(void *virt_addr, size_t len)
{
	//munmap(virt_addr, len);
	unmap_physical_addr_range(virt_addr,len);
}

void physunmap_unaligned(void *virt_addr, size_t len)
{
	/* No need to check for zero size, such mappings would have yielded ERROR_PTR. */
	if (virt_addr == ERROR_PTR) {
		msg_perr("Trying to unmap a nonexisting mapping!\n"
			 "Please report a bug at flashrom@flashrom.org\n");
		return;
	}

	sys_physunmap_unaligned(virt_addr, len);
}

void physunmap(void *virt_addr, size_t len)
{
	uintptr_t tmp;

	/* No need to check for zero size, such mappings would have yielded ERROR_PTR. */
	if (virt_addr == ERROR_PTR) {
		msg_perr("Trying to unmap a nonexisting mapping!\n"
			 "Please report a bug at flashrom@flashrom.org\n");
		return;
	}
	tmp = (uintptr_t)virt_addr;
	/* We assume that the virtual address of a page-aligned physical address is page-aligned as well. By
	 * extension, rounding a virtual unaligned address as returned by physmap should yield the same offset
	 * between rounded and original virtual address as between rounded and original physical address.
	 */
	round_to_page_boundaries(&tmp, &len);
	virt_addr = (void *)tmp;
	physunmap_unaligned(virt_addr, len);
}




// ---   read  and  write  
void mmio_writeb(uint8_t val, void *addr)
{
	*(volatile uint8_t *) addr = val;
	//sync_primitive();
}

void mmio_writew(uint16_t val, void *addr)
{
	*(volatile uint16_t *) addr = val;
	//sync_primitive();
}

void mmio_writel(uint32_t val, void *addr)
{
	*(volatile uint32_t *) addr = val;
	//sync_primitive();
}

uint8_t mmio_readb(const void *addr)
{
	return *(volatile const uint8_t *) addr;
}

uint16_t mmio_readw(const void *addr)
{
	return *(volatile const uint16_t *) addr;
}

uint32_t mmio_readl(const void *addr)
{
	return *(volatile const uint32_t *) addr;
}

void mmio_readn(const void *addr, uint8_t *buf, size_t len)
{
	memcpy(buf, addr, len);
	return;
}

void mmio_le_writeb(uint8_t val, void *addr)
{
	mmio_writeb(cpu_to_le8(val), addr);
}

void mmio_le_writew(uint16_t val, void *addr)
{
	mmio_writew(cpu_to_le16(val), addr);
}

void mmio_le_writel(uint32_t val, void *addr)
{
	mmio_writel(cpu_to_le32(val), addr);
}

uint8_t mmio_le_readb(const void *addr)
{
	return le_to_cpu8(mmio_readb(addr));
}

uint16_t mmio_le_readw(const void *addr)
{
	return le_to_cpu16(mmio_readw(addr));
}

uint32_t mmio_le_readl(const void *addr)
{
	return le_to_cpu32(mmio_readl(addr));
}

enum mmio_write_type {
	mmio_write_type_b,
	mmio_write_type_w,
	mmio_write_type_l,
};

struct undo_mmio_write_data {
	void *addr;
	int reg;
	enum mmio_write_type type;
	union {
		uint8_t bdata;
		uint16_t wdata;
		uint32_t ldata;
	};
};

static int undo_mmio_write(void *p)
{
	struct undo_mmio_write_data *data = p;
	msg_pdbg("Restoring MMIO space at %p\n", data->addr);
	switch (data->type) {
	case mmio_write_type_b:
		mmio_writeb(data->bdata, data->addr);
		break;
	case mmio_write_type_w:
		mmio_writew(data->wdata, data->addr);
		break;
	case mmio_write_type_l:
		mmio_writel(data->ldata, data->addr);
		break;
	}
	/* p was allocated in register_undo_mmio_write. */
	free(p);
	return 0;
}

#define register_undo_mmio_write(a, c)					\
{									\
	struct undo_mmio_write_data *undo_mmio_write_data;		\
	undo_mmio_write_data = malloc(sizeof(*undo_mmio_write_data));	\
	if (!undo_mmio_write_data) {					\
		msg_gerr("Out of memory!\n");				\
		exit(1);						\
	}								\
	undo_mmio_write_data->addr = a;					\
	undo_mmio_write_data->type = mmio_write_type_##c;		\
	undo_mmio_write_data->c##data = mmio_read##c(a);		\
	register_shutdown(undo_mmio_write, undo_mmio_write_data);	\
}

#define register_undo_mmio_writeb(a) register_undo_mmio_write(a, b)
#define register_undo_mmio_writew(a) register_undo_mmio_write(a, w)
#define register_undo_mmio_writel(a) register_undo_mmio_write(a, l)

void rmmio_writeb(uint8_t val, void *addr)
{
	register_undo_mmio_writeb(addr);
	mmio_writeb(val, addr);
}

void rmmio_writew(uint16_t val, void *addr)
{
	register_undo_mmio_writew(addr);
	mmio_writew(val, addr);
}

void rmmio_writel(uint32_t val, void *addr)
{
	register_undo_mmio_writel(addr);
	mmio_writel(val, addr);
}

void rmmio_le_writeb(uint8_t val, void *addr)
{
	register_undo_mmio_writeb(addr);
	mmio_le_writeb(val, addr);
}

void rmmio_le_writew(uint16_t val, void *addr)
{
	register_undo_mmio_writew(addr);
	mmio_le_writew(val, addr);
}

void rmmio_le_writel(uint32_t val, void *addr)
{
	register_undo_mmio_writel(addr);
	mmio_le_writel(val, addr);
}

void rmmio_valb(void *addr)
{
	register_undo_mmio_writeb(addr);
}

void rmmio_valw(void *addr)
{
	register_undo_mmio_writew(addr);
}

void rmmio_vall(void *addr)
{
	register_undo_mmio_writel(addr);
}


DEBUG_LEVEL get_dbg_level (void)
{
  return g_dbg_lvl;
}

void set_dbg_level (DEBUG_LEVEL dbg_level)
{
  DBG_INFO ("Set Debug Level as %d\n", dbg_level);

  g_dbg_lvl = dbg_level;
  return;
}
/*struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device)
{
	struct pci_dev *temp;
	struct pci_filter filter;

	pci_filter_init(NULL, &filter);
	filter.vendor = vendor;
	filter.device = device;

	for (temp = pacc->devices; temp; temp = temp->next)
		if (pci_filter_match(&filter, temp))
			return temp;

	return NULL;
}*/

/*
 *
 */
/*struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
			      uint16_t card_vendor, uint16_t card_device)
{
	struct pci_dev *temp;
	struct pci_filter filter;

	pci_filter_init(NULL, &filter);
	filter.vendor = vendor;
	filter.device = device;

	for (temp = pacc->devices; temp; temp = temp->next)
		if (pci_filter_match(&filter, temp)) {
			if ((card_vendor == pci_read_word(temp, 0x2C)) &&
			    (card_device == pci_read_word(temp, 0x2E)))
				return temp;
		}

	return NULL;
}*/
// ====================================================================================
